package com.basker.pisces.domain.entity.data;

import java.util.List;
import java.util.Map;

import org.springframework.lang.Nullable;

import com.basker.pisces.core.exception.StaleObjectException;
import com.basker.pisces.core.repository.IDataRepository;
import com.basker.pisces.domain.annotation.PropertyOutput;
import com.basker.pisces.domain.entity.meta.ChildEntity;
import com.basker.pisces.domain.entity.meta.Entity;
import com.basker.pisces.domain.property.InvalidPropertyValueException;
import com.basker.pisces.domain.property.meta.Property;
import com.basker.pisces.domain.value.IValueObject;

/**
 * 封装数据对象的实体，提供操作数据对象的方法
 *
 * @author hangwen
 */
public interface IDataEntity {

    /**
     * 为子根对象实体添加数据对象
     *
     * @param childEntity
     * @param childObjectList
     */
    void addChildObjectList(ChildEntity childEntity, List<Object> childObjectList);

    /**
     * 创建一个子对象，并添加到集合里
     *
     * @param childEntityName
     * @return
     */
    Object addNewChildObject(String childEntityName);

    /**
     * 批量录入数据
     *
     * @param inputObjects,一个pojo对象,字段通过{@link PropertyInput}与当前实体的属性绑定
     * @throws InvalidPropertyValueException
     */
    List<Object> batchInput(List<?> inputObjects) throws InvalidPropertyValueException;

    /**
     * 批量录入数据
     *
     * @param inputObjects,一个pojo对象,字段通过{@link PropertyInput}与当前实体的属性绑定
     * @param option                           导入选项，可以为null
     * @throws InvalidPropertyValueException
     */
    List<Object> batchInput(List<?> inputObjects, @Nullable InputOption option) throws InvalidPropertyValueException;

    /**
     * 批量填充到outputType对应的对象中
     *
     * @param <T>
     * @param dataObjects
     * @param outputType
     * @return
     */
    <T> List<T> batchOutput(List<Object> dataObjects, Class<T> outputType);

    /**
     * 清空子实体数据列表
     *
     * @param childEntityName
     */
    void clearChildObjectList(String childEntityName);

    Object createChildObject(String childEntityName);

    /**
     * 创建一个新的数据对象
     *
     * @return
     */
    Object createDataObject();

    /**
     * 获取根对象子实体的数据对象集合
     *
     * @param childEntityName
     * @return
     */
    List<Object> getChildObjectList(String childEntityName);

    /**
     * 返回数据对象，它是对应实体的聚合根，可能返回null
     *
     * @return
     */
    Object getDataObject();

    /**
     * 设置数据对象，它是对应实体的聚合根
     *
     * @param dataObject
     */
    void setDataObject(Object dataObject);

    /**
     * 获取数据仓库接口
     *
     * @return
     */
    IDataRepository getDataRepository();

    /**
     * 对应的实体对象
     *
     * @return
     */
    Entity getEntity();

    /**
     * 获取聚合根对象的属性值
     *
     * @param property
     * @return
     */
    <TOutValue extends IValueObject> TOutValue getPropertyValue(Property<TOutValue> property);

    /**
     * 获取指定数据对象的属性值，比如需要访问子实体数据对象的时候可以调用此方法，需要确保数据对象和属性要相匹配
     *
     * @param property
     * @param targetObject
     * @return
     */
    <TOutValue extends IValueObject> TOutValue getPropertyValue(Property<TOutValue> property, Object targetObject);

    /**
     * 获取聚合根对象的属性值
     *
     * @param propertyAlias
     * @return
     */
    IValueObject getPropertyValue(String propertyAlias);

    /**
     * 将inputObject数据填充到当前的数据实体中
     *
     * @param inputObject,一个pojo对象,字段通过{@link PropertyInput}与当前实体的属性绑定
     * @return
     * @throws InvalidPropertyValueException
     */
    Object input(Object inputObject) throws InvalidPropertyValueException;

    /**
     * 将inputObject数据填充到当前的数据实体中
     *
     * @param inputObject,一个pojo对象,字段通过{@link PropertyInput}与当前实体的属性绑定
     * @param option                          导入选项,设置一些导入规则
     * @return
     * @throws InvalidPropertyValueException
     */
    Object input(Object inputObject, InputOption option) throws InvalidPropertyValueException;

    /**
     * 填充指定的子实体
     *
     * @param childEntityName
     * @param inputObjects
     * @throws InvalidPropertyValueException
     */
    List<Object> inputChildren(String childEntityName, List<?> inputObjects)
            throws InvalidPropertyValueException;

    /**
     * 填充指定的子实体
     *
     * @param childEntityName
     * @param inputObjects
     * @param option
     * @throws InvalidPropertyValueException
     */
    List<Object> inputChildren(String childEntityName, List<?> inputObjects, InputOption option)
            throws InvalidPropertyValueException;

    /**
     * 加载指定id的数据
     *
     * @param pkValue
     * @return
     * @throws StaleObjectException 找不到数据会抛出此异常
     */
    Object loadDataObject(Object pkValue) throws StaleObjectException;

    /**
     * 将当前的dataObject填充到outputType对应的对象中
     *
     * @param <T>
     * @param outputType 一个pojo类型,字段通过{@link PropertyOutput}与实体的属性绑定
     * @return
     */
    <T> T output(Class<T> outputType);

    /**
     * 将指定dataObject填充到outputType对应的对象中
     *
     * @param <T>
     * @param outputType 一个pojo类型,字段通过{@link PropertyOutput}与实体的属性绑定
     * @return
     * @parm dataObject
     */
    <T> T output(Object dataObject, Class<T> outputType);

    Map<String, Object> outputAsMap();

    Map<String, Object> outputAsMap(Object dataObject);

    Map<String, Object> outputAsMap(Entity entity, Object dataObject);

    /**
     * 为根对象的子实体移除数据对象
     *
     * @param childEntityName
     * @param childObjectList
     */
    void removeChildObjectList(String childEntityName, List<Object> childObjectList);

    /**
     * 为聚合根对象设置属性值
     *
     * @param property
     * @param inputValue
     * @throws InvalidPropertyValueException 输入值与当前属性不匹配会抛出此异常
     */
    void setPropertyValue(Property<?> property, Object inputValue) throws InvalidPropertyValueException;

    /**
     * 为指定数据对象的属性赋值，比如需要访问子实体数据对象的时候可以调用此方法，需要确保数据对象和属性要相匹配
     *
     * @param property
     * @param inputValue
     * @throws InvalidPropertyValueException 输入值与当前属性不匹配会抛出此异常
     */
    void setPropertyValue(Property<?> property, Object targetObject, Object inputValue)
            throws InvalidPropertyValueException;

    /**
     * 为聚合根对象设置属性值
     *
     * @param propertyAlias
     * @param inputValue
     * @throws InvalidPropertyValueException
     */
    void setPropertyValue(String propertyAlias, Object inputValue) throws InvalidPropertyValueException;
}
